package org.light.framework.orm;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import org.light.framework.orm.annotation.Column;
import org.light.framework.orm.annotation.PrimaryKey;
import org.light.framework.orm.annotation.Table;
import org.light.framework.util.StringUtil;

/**
 * 数据库关系映射查询类
 * 
 * @author de.cel
 *
 */
public class DbMapper {

	/**
	 * 获取实体映射的表名
	 */
	public static final String PREFIX_TABLE_ALIAS = "t";

	/// <summary>
	/// 主表别名
	/// </summary>
	public static final String PRIMARY_TABLE_ALIAS = PREFIX_TABLE_ALIAS + "0";

	/**
	 * 获取实体映射的表名
	 * 
	 * @param t
	 *            类型
	 * @return 数据表名称
	 */
	public static String getTableName(Class<?> t) throws Exception {

		Table table = t.getAnnotation(Table.class);

		if (table != null)
			return table.name();
		else
			throw new Exception("实体未添加 Table 映射");
	}

	/**
	 * 获取实体映射的主键
	 * 
	 * @param t
	 *            类型
	 * @return 主键
	 */
	public static ColumnInfo getPrimaryKey(Class<?> c) throws Exception {

		Field[] fields = c.getDeclaredFields();

		for (Field f : fields) {

			PrimaryKey column = f.getAnnotation(PrimaryKey.class);
			if (column != null) {

				ColumnInfo col = new ColumnInfo();

				col.name = column.name();
				col.dbType = column.dbType();
				col.propertyName = f.getName();

				return col;
			}
		}

		return null;
	}

	private static ColumnInfo getPrimaryKey(Field p) {
		PrimaryKey pk = p.getAnnotation(PrimaryKey.class);
		if (pk == null) {
			return null;
		} else {
			ColumnInfo column = new ColumnInfo();

			column.propertyName = p.getName();
			column.name = pk.name();
			column.dbType = pk.dbType();
			column.length = pk.length();

			if (column.name == null || column.name.isEmpty()) // 如果没有指定字段名，则取属性名
			{
				column.name = column.propertyName;
			}
			return column;
		}
	}

	/**
	 * 获取实体列映射
	 * 
	 * @param t
	 *            类型
	 * @return 数据列
	 */
	public static ArrayList<ColumnInfo> getColumns(Class<?> c) throws Exception {

		ArrayList<ColumnInfo> columns = new ArrayList<ColumnInfo>();

		columns.add(DbMapper.getPrimaryKey(c));

		Field[] fields = c.getDeclaredFields();

		for (Field f : fields) {

			Column column = f.getAnnotation(Column.class);
			if (column != null) {
				ColumnInfo col = new ColumnInfo();

				col.name = column.name();
				col.dbType = column.dbType();
				col.length = column.length();
				col.cascade = column.cascade();
				col.propertyName = f.getName();

				columns.add(col);
			}
		}
		return columns;
	}

	/**
	 * 获取实体列映射
	 * 
	 * @param t
	 *            类型
	 * @return 数据列
	 */

	public static <T> LinkedHashMap<BelongsInfo, Object> getBelongs(T obj) throws Exception {

		LinkedHashMap<BelongsInfo, Object> columns = new LinkedHashMap<org.light.framework.orm.BelongsInfo, Object>();
		Field[] fields = obj.getClass().getDeclaredFields();

		for (Field f : fields) {

			org.light.framework.orm.annotation.Belongs column = f
					.getAnnotation(org.light.framework.orm.annotation.Belongs.class);
			if (column != null) {
				BelongsInfo col = new org.light.framework.orm.BelongsInfo();

				col.name = column.name();
				col.joinBehaviour = column.joinBehaviour();
				col.recursiveDeep = column.recursiveDeep();
				col.readOnly = column.readOnly();
				col.propertyName = f.getName();

				// 获取外键关联对象的ID值
				f.setAccessible(true);
				Object entity = f.get(obj);
				Object fkValue = null;
				if (entity != null) {
					org.light.framework.orm.ColumnInfo p = DbMapper.getPrimaryKey(entity.getClass());
					Field fkField=entity.getClass().getDeclaredField(p.propertyName);
					fkField.setAccessible(true);
					fkValue = fkField.get(entity);
				}
				columns.put(col, fkValue);
			}
		}
		return columns;
	}

	static int deep = 0;

	public static LinkedHashMap<String, BelongsInfo> getBelongsDic(Class<?> type) {

		// 重置深度
		deep = 0;
		return getBelongsDic(type, null);
	}

	private static ColumnInfo getColumn(Field p, int cascade) {

		org.light.framework.orm.ColumnInfo column = null;
		org.light.framework.orm.annotation.Column attr = p
				.getAnnotation(org.light.framework.orm.annotation.Column.class);

		if (attr != null) {
			if (attr.cascade() >= cascade) {
				column = new org.light.framework.orm.ColumnInfo();
				column.propertyName = p.getName();
				column.name = attr.name();
				column.dbType = attr.dbType();
				column.length = attr.length();
				// column.readOnly = attr.readOnly;
				// column.IsAutoIncrement = attr.IsAutoIncrement;

				if (column.name == null || column.name.isEmpty()) // 如果没有指定字段名，则取属性名
				{
					column.name = p.getName();
				}
			}
		}
		return column;
	}

	/// <summary>
	/// 获取属性对应的列表
	/// </summary>
	/// <param name="type"></param>
	/// <param name="propertyName"></param>
	/// <returns></returns>
	public static String getColumnName(Class<?> type, String propertyName) throws Exception {
		String columnName = "";
		String aliasStr = "";

		LinkedHashMap<String, ColumnInfo> dicColumns = getColumnsDic(type);
		LinkedHashMap<String, BelongsInfo> dicBelongs = getBelongsDic(type);

		if (dicBelongs != null && dicBelongs.size() > 0) {
			aliasStr = DbMapper.PRIMARY_TABLE_ALIAS + ".";
		}
		if (dicColumns.containsKey(propertyName)) {
			columnName = (aliasStr + dicColumns.get(propertyName).name);
		} else if (dicBelongs.containsKey(propertyName)) {
			BelongsInfo bc = dicBelongs.get(propertyName); // 找到对应的外键信息
			columnName = (DbMapper.PRIMARY_TABLE_ALIAS + "." + bc.name);
		} else if (propertyName.contains(".")) {
			String[] temp = propertyName.split("\\."); // Aaa.Bbb.Ccc =>
														// Aaa.Bbb、Ccc
			String lastPName = temp[temp.length - 1]; // Ccc
			String firstPName = propertyName.substring(0, propertyName.lastIndexOf(".")); // Aaa.Bbb

			if (dicBelongs.containsKey(firstPName)) {
				BelongsInfo bc = dicBelongs.get(firstPName); // 找到对应的外键信息（表别名）

				// 找到外键表的列
				int cascade = propertyName.split("\\.").length;
				ArrayList<ColumnInfo> bcColumns = DbMapper.getColumns(bc.entityType, cascade);
				ColumnInfo bcColumn = ColumnInfo.findByName(bcColumns, lastPName); 
				if (bcColumn != null)
					columnName = (bc.alias + "." + bcColumn.name);
				else
					throw new Exception("不支持按 " + propertyName + " 查询");
			}
		}
		if (StringUtil.isNullOrEmpty(columnName)) {
			columnName = propertyName;
		}
		return columnName;
	}

	public static ArrayList<ColumnInfo> getColumns(Class<?> type, int cascade) {

		ArrayList<ColumnInfo> columns = new ArrayList<ColumnInfo>();
		Field[] properties = type.getDeclaredFields();
		for (Field p : properties) {
			PrimaryKey pk = p.getAnnotation(PrimaryKey.class);
			if (pk != null) {
				ColumnInfo column = getPrimaryKey(p);
				if (column != null)
					columns.add(column);
			} else {
				org.light.framework.orm.ColumnInfo field = getColumn(p, cascade);
				if (field != null)
					columns.add(field);
			}
		}

		return columns;
	}

	private static BelongsInfo getBelongs(Field p, int idx, BelongsInfo refer) {
		BelongsInfo column = null;
		org.light.framework.orm.annotation.Belongs attr = p
				.getAnnotation(org.light.framework.orm.annotation.Belongs.class);

		if (attr != null) {

			column = new BelongsInfo();
			column.name = attr.name();
			column.entityType = p.getType(); // 如：Mechant
			column.joinBehaviour = attr.joinBehaviour();
			column.recursiveDeep = attr.recursiveDeep();
			column.readOnly = attr.readOnly();

			if (refer != null) {
				column.propertyName = refer.propertyName + "." + p.getName(); // 如：
																				// Agent.Basic
				column.aliasReference = refer.alias;
				column.alias = refer.alias + "_" + PREFIX_TABLE_ALIAS + idx; // 如：t1_t0
			} else {
				column.propertyName = p.getName();
				column.alias = PREFIX_TABLE_ALIAS + (idx + 1);
			}
		}
		return column;
	}

	private static LinkedHashMap<String, BelongsInfo> getBelongsDic(Class<?> type, BelongsInfo refer) {
		LinkedHashMap<String, BelongsInfo> columns = new LinkedHashMap<String, BelongsInfo>();
		Field[] fields = type.getDeclaredFields();
		int idx = 0;
		for (Field f : fields) {

			org.light.framework.orm.annotation.Belongs belongs = f
					.getAnnotation(org.light.framework.orm.annotation.Belongs.class);
			if (belongs != null) {
				BelongsInfo column = getBelongs(f, idx++, refer);
				if (column != null && !columns.containsKey(column.propertyName)) {
					columns.put(column.propertyName, column);

					// 避免无限递归
					if (column.entityType != type || deep < column.recursiveDeep) {
						if (column.entityType == type)
							deep++;

						// 递归
						LinkedHashMap<String, BelongsInfo> subColumns = getBelongsDic(column.entityType, column);
						if (subColumns != null && !subColumns.isEmpty()) {
							for (Entry<String,BelongsInfo> entry : subColumns.entrySet()) {
								columns.put(entry.getKey(), entry.getValue());
							}
						}
					}
				}
			}
		}
		return columns;
	}

	public static LinkedHashMap<String, ColumnInfo> getColumnsDic(Class<?> type) throws Exception {
		LinkedHashMap<String, ColumnInfo> columns = new LinkedHashMap<String, ColumnInfo>();
		ArrayList<ColumnInfo> infos = getColumns(type);
		for (ColumnInfo columnInfo : infos) {
			columns.put(columnInfo.propertyName, columnInfo);
		}
		return columns;
	}

	public static LinkedHashMap<String, ManyToManyInfo> getManyToMany(Class<?> c) {

		return null;
	}
}
